﻿using System;
using System.Data.Linq;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using eCRAM.Dao;
using eCRAM.Tools;
using eCRAM.WebSite.Models;
using eCRAM.Model;
using eCRAM.Bll;
using eCRAM.WebSite.Services;
using eCRAM.WebSite.Attributes;
using Microsoft.Practices.ObjectBuilder2;


namespace eCRAM.WebSite.Controllers
{
    [CustomAuthorization(UserRole.Engineer, UserRole.SubContractor, UserRole.ProjectLeader, UserRole.Administrator)]
    public class CramController : Controller
    {

        public ActionResult Index()
        {
            var userId = ((SessionData)Session["MySessionData"]).CurrentUser.Id;
            return CramDetails(DateTime.Now.Month, DateTime.Now.Year, userId);
        }

        //
        // GET: /Cram/Details/5

        public ActionResult Details(Guid id)
        {
            return View("CramDetails");
        }


        public ActionResult EditCram(int month, int year, Guid userId)
        {
            return CramDetails(month, year, userId);
        }

        private ActionResult DisplayCramDetails(CramModels model)
        {
            return View("CramDetails", model);
        }


        public ActionResult CramDetails(int month, int year, Guid userId)
        {
            var cram = CramManager.FindCram(userId, month, year);
            if (cram == null)
            {
                cram = new Cram
                {
                    Id = Guid.NewGuid(),
                    UserId = userId,
                    Year = year,
                    Month = month,
                    CreationDate = DateTime.Now,
                    LastModificationDate = DateTime.Now
                };

                CramManager.InsertCram(cram);
                cram = CramManager.FindCram(userId, month, year);
            }
            var model = ControllerService.GetCramModel(cram);

            return DisplayCramDetails(model);
        }

        public ActionResult DownloadAttachment(string attachmentName, int month, int year, Guid userId)
        {

            var cram = CramManager.FindCram(userId, month, year);

            if (!String.IsNullOrEmpty(attachmentName))
            {
                var attachement = CramManager.GetAttachmentByCramIdAndName(cram.Id, attachmentName);
                if(attachement != null)
                {
                    return File(attachement.Cram_AttachmentData.Data.ToArray(), "content-data", attachement.AttachmentName);
                }
            }

            return DisplayCramDetails( ControllerService.GetCramModel(cram));
            
        }

        [HttpPost]
        public ActionResult AddAttachment(HttpPostedFileBase attachment, int month, int year, Guid userId, FormCollection formCollection)
        {
            var cram = CramManager.FindCram(userId, month, year);
            var model = ControllerService.GetCramModel(cram);
            var postedFile = attachment;

            if (postedFile!= null && postedFile.ContentLength > 0)
            {
                //   postedFile.SaveAs(path);
                var attachmentToSave = new Cram_Attachment
                {
                    Id = Guid.NewGuid(),
                    CramId = cram.Id,
                    AttachmentName = postedFile.FileName
                };

                var attachmentDataToSave = new Cram_AttachmentData { AttachementId = Guid.NewGuid() };
                using (var reader = new BinaryReader(postedFile.InputStream))
                {
                    var arrDataToSave = new byte[postedFile.ContentLength];
                    reader.Read(arrDataToSave, 0, arrDataToSave.Length);
                    attachmentDataToSave.Data = new Binary(arrDataToSave);
                }

                attachmentToSave.Cram_AttachmentData = attachmentDataToSave;

                CramManager.InsertAttachment(attachmentToSave);
            }

            return DisplayCramDetails(model);
        }



        [HttpPost]
        public ActionResult DeleteAttachment(string attachmentName, int month, int year, Guid userId, FormCollection formCollection)
        {
            var cram = CramManager.FindCram(userId, month, year);

            if (!String.IsNullOrEmpty(attachmentName))
            {
                CramManager.DeleteAttachment(
                    cram.Cram_Attachments.Where(item => item.AttachmentName == attachmentName).FirstOrDefault());
                cram = CramManager.FindCram(userId, month, year);


                var destinationFolder = Server.MapPath("/Attachments");
                var fileName = Path.GetFileName(attachmentName);

                var path = Path.Combine(destinationFolder, fileName);
                var info = new FileInfo(path);
                info.Delete();
            }
            var model = ControllerService.GetCramModel(cram);
            return DisplayCramDetails(model);
        }


        [HttpPost]
        public ActionResult ValidateCram(int month, int year, Guid userId, string comment, FormCollection formCollection)
        {
            var cram = CramManager.FindCram(userId, month, year);

            //Sauvegarde du cram 
            Save(cram, comment, formCollection);

            var model = ControllerService.GetCramModel(cram);
            return DisplayCramDetails(model);
        }

        [HttpPost]
        public ActionResult PrintCram(int month, int year, Guid userId, string comment, FormCollection formCollection)
        {
            var cram = CramManager.FindCram(userId, month, year);

            //Sauvegarde du cram 
            Save(cram, comment, formCollection);

            var selectedId =
                formCollection.AllKeys.Where(c => c.StartsWith("selection_"))
                .ToDictionary(c => new Guid(c.Replace("selection_", "")), c => formCollection[c]);

            var vs = selectedId.Where(c => selectedId.Values.Where(v => v.StartsWith("true")).Contains(c.Value)).ToDictionary(c => c.Key, v => v.Value);

            var printCram = new Cram
            {
                Id = cram.Id,
                CreationDate = cram.CreationDate,
                LastModificationDate = cram.LastModificationDate,
                Month = cram.Month,
                Comment = cram.Comment,
                User = cram.User,
                UserId = cram.UserId,
                Year = cram.Year
            };
            printCram.CramLines.AddRange(cram.CramLines.Where(l => vs.Keys.Contains(l.Id)));

            var model = ControllerService.GetCramModel(printCram);
            var printModel = ControllerService.GetPrintCramModel(model);
            return View("PrintCram", printModel);
        }

        [HttpPost]
        public ActionResult SubmitCram(int month, int year, Guid userId, string comment, FormCollection formCollection)
        {
            var cram = CramManager.FindCram(userId, month, year);

            //Sauvegarde du cram 
            Save(cram, comment, formCollection);

            // Recupère le cram sauvegardé
            cram = CramManager.FindCram(userId, month, year);

            // détermine si le cram possède au moins une activité
            var activitiesCountIsValid = cram.CramLines.Any();

            // calcule le total des jours travaillés hors jour fériés et week end ( dans les jours ouvrés)
            var workedDaysTotal = cram.CramLines
                .Where(cl => !cl.Activity.ActivityType.IsHoliday && !cl.Activity.IsAstreinte)
                .Sum(
                    item => item.Cram_DailyActivities.Where(da => !WorkingDay.IsHoliday(da.Day)).Sum(d => d.Value)
                );

            // calcule le total des jours de congés dans les jours ouvrés
            var vacationDaysTotal = cram.CramLines
                .Where(cl => cl.Activity.ActivityType.IsHoliday)
                .Sum(cl => cl.Cram_DailyActivities.Where(da => !WorkingDay.IsHoliday(da.Day)).Sum(d => d.Value));

            // calcule le nombre de jours ouvrés
            var workingDaysTotal = WorkingDay.MonthWorkingDays(year, month).Where(b => b).Count();

            // calcule si la somme des jours travaillés et la somme des jours de congés dans les jours ouvrés 
            // est égal au total des jours ouvrés
            var workingDaysIsValid = (workedDaysTotal + vacationDaysTotal) == workingDaysTotal;

            if (cram.Status != CramStatus.New)
            {
                ModelState.AddModelError("SubmitCram", "Le cram doit être au statut 'Nouveau' pour être soumis.");
            }
            else if (activitiesCountIsValid && workingDaysIsValid)
            {
                cram.CramLines.ForEach(l => { l.Status = (int)CramStatus.Submit; CramLineRepository.Update(l); });
                // CramRepository.Update(cram);
            }
            else
            {
                string message;
                if (!activitiesCountIsValid)
                {
                    message = "Il doit y avoir au moins une activité associée au CRAM.";
                }
                else
                {
                    message = "La somme des imputations sur le mois (congés compris) doit être égal au nombre de jours ouvrés du mois.";
                }
                ModelState.AddModelError("SubmitCram", message);
            }

            var model = ControllerService.GetCramModel(cram);
            return DisplayCramDetails(model);
        }


        private void Save(Cram cram, string comment, FormCollection formCollection)
        {
            // Récupération des id des activités de chaque ligne
            var listActivitiesId =
               formCollection.AllKeys.Where(c => c.StartsWith("idCramLine_"))
               .ToDictionary(c => new Guid(c.Replace("idCramLine_", "")), c => new Guid(formCollection[c]));

            var cramLines = listActivitiesId.Select(item =>
            {
                var cramLine = new CramLine { CramId = cram.Id, ActivityId = item.Key, Id = item.Value, Status = cram.Status == 0 ? (int)CramStatus.New : (int)cram.Status };
                if (item.Value == Guid.Empty)
                {
                    cramLine.Id = Guid.NewGuid();
                }

                var regex = string.Format("^day_[0-9]*_{0}$", item.Key);

                var listDayValueByActivity =
                    formCollection.AllKeys
                    .Where(c => Regex.IsMatch(c, regex) && !string.IsNullOrEmpty(formCollection[c]))
                    .Select(c => new Cram_DailyActivity
                    {
                        Id = Guid.NewGuid(),
                        CramLineId = cramLine.Id,
                        Day = new DateTime(cram.Year, cram.Month, int.Parse(c.Replace("day_", "").Replace("_" + item.Key, ""))),
                        Value = decimal.Parse(formCollection[c].Replace(".", CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator)),
                    });

                cramLine.Cram_DailyActivities.AddRange(listDayValueByActivity);
                return cramLine;
            });

            cram.LastModificationDate = DateTime.Now;
            cram.Comment = comment;
            CramRepository.Update(cram);

            CramLineRepository.DeleteByCramId(cram.Id);
            CramLineRepository.InsertCramLines(cramLines.Where(x => x.Cram_DailyActivities.Any()));
        }

        [HttpPost]
        public ActionResult CancelPrint(int month, int year, Guid userId)
        {
            var cram = CramManager.FindCram(userId, month, year);
            return RedirectToAction("CramDetails", new { cram.Month, cram.Year, cram.UserId });
        }


        [HttpPost]
        public ActionResult SaveCram(int month, int year, Guid userId, string comment, FormCollection formCollection)
        {
            var cram = CramManager.FindCram(userId, month, year);
            Save(cram, comment, formCollection);

            return RedirectToAction("CramDetails", new { cram.Month, cram.Year, cram.UserId });
        }



        [HttpPost]
        public ActionResult ExportToPDF(int month, int year, Guid userId)
        {
            var generator = new CramExcelFileGenerator(CramManager.FindCram(userId, month, year));
            generator.Generate();

            var fileName = generator.FileName;

            if (string.IsNullOrEmpty(fileName))
            {
                return CramDetails(month, year, userId);
            }
            
            var pdfFile = PrintToPdf(fileName);
            return File(GetFile(pdfFile), MediaTypeNames.Application.Octet, Path.GetFileName(pdfFile));
        }

        [HttpPost]
        public ActionResult ExportAstreintesToPDF(int month, int year, Guid userId)
        {
            var generator = new AstreinteFileGenerator(CramManager.FindCram(userId, month, year));
            generator.Generate();

            var fileName = generator.FileName;
            
            if(string.IsNullOrEmpty(fileName))
            {
                return CramDetails(month, year, userId);
            }

            var pdfFile = PrintToPdf(fileName);
            return File(GetFile(pdfFile), MediaTypeNames.Application.Octet, Path.GetFileName(pdfFile));
        }

        /// <summary>
        /// Imprime le fichier dans un PDF
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private string PrintToPdf(string fileName)
        {
            return fileName;

            // Ce code a été mis en commentaire car nous rencontrons des pb pour l'export des xls en pdf (du a l'automation via IIS ce qui n'est pas top)
            //var pdfFileName = Path.Combine(Path.GetDirectoryName(fileName), string.Format("{0}.{1}", Path.GetFileNameWithoutExtension(fileName), "pdf"));
            //ExcelToPdf.ConvertToPdf(fileName, pdfFileName);
            //return pdfFileName;
        }

        /// <summary>
        /// Récupére un tableau d'octet correspondant au contenu du fichier
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private byte[] GetFile(string fileName)
        {
            using (var fs = System.IO.File.OpenRead(fileName))
            {
                var data = new byte[fs.Length];
                var br = fs.Read(data, 0, data.Length);
                if (br != fs.Length)
                    throw new IOException(fileName);
                return data;
            }
        }


    }
}
